/*

Archivo: "main.cpp"

Archivo principal del programa "tp1".
Implementación del TDA conjunto.

Primera versión: 5-6-2012

*/


#include <cstdlib>
#include <fstream>
#include <string>

#include "globals.h"
#include "cmdline.h"
#include "abstractor.h"

/*============================= Constantes del main -  Begin ============================= */

#define PROGRAM_NAME            "tp1"
#define PROGRAM_DESCRIPTION     "TDA Set implementation"
#define PROGRAM_VERSION         {0, 9, 2}

/*============================= Constantes del main -  End ============================= */


using namespace std;



/*---------------- Funciones de procesado de la línea de comando - Begin ----------------*/

// Procesa la opción de línea de comando "-i".
static void opt_istream(Parameters_Queue_t &args);

// Procesa la opción de línea de comando "-o".
static void opt_ostream(Parameters_Queue_t &args);

// Procesa la opción de línea de comando "-I".
static void opt_implementation(Parameters_Queue_t &args);

// Procesa la opción de línea de comando "-t".
static void opt_type(Parameters_Queue_t &args);

/*---------------- Funciones de procesado de la línea de comando - End ----------------*/


// Puntero al stream de entrada.
static istream *iss = NULL;
// Puntero al stream de salida.
static ostream *oss = NULL;
// Archivo de entrada (stream).
static fstream ifs;
//Archivo de salida (stream).
static fstream ofs;
// Implementación solicitada.
static t_implementations implementation = timp_list;
// Tipo de dato solicitado.
static t_datatypes datatype = tdtype_int;


/*---------------- Funciones de procesado de la línea de comando - Begin ----------------*/

// Procesa la opción de línea de comando "-i".
// Se espera que la misma retire los argumentos de la cola.
static void opt_istream(Parameters_Queue_t &args)
{
    // Tomo el único parámetro que
    // necesito ( nombre sel archivo).
    string arg = args.front();

    // Elimino de la cola
    // el parámtro consumido.
    args.pop();

    // Si el nombre del archivos es "-", usaremos la entrada
    // estándar. De lo contrario, abrimos un archivo en modo
    // de lectura.
    if (arg == "-")
    {
        iss = &cin;
    }
    else
    {
        ifs.open(arg.c_str(), ios::in);
        iss = &ifs;
    }

    // Verificamos que el stream este OK.
    if (!iss->good())
    {
        cerr << ERROR_CANNOT_OPEN
             << arg
             << DOT
             << endl;
        exit(ERROR_NUMBER_CANNOT_OPEN);
    }
}

// Procesa la opción de línea de comando "-o".
// Se espera que la misma retire los argumentos de la cola.
static void opt_ostream(Parameters_Queue_t &args)
{
    // Tomo el único parámetro que
    // necesito ( nombre sel archivo).
    string arg = args.front();

    // Elimino de la cola
    // el parámtro consumido.
    args.pop();

    // Si el nombre del archivos es "-", usaremos la salida
    // estándar. De lo contrario, abrimos un archivo en modo
    // de escritura.
    if (arg == "-")
    {
        oss = &cout;
    }
    else
    {
        ofs.open(arg.c_str(), ios::out);
        oss = &ofs;
    }

    // Verificamos que el stream este OK.
    if (!oss->good())
    {
        cerr << ERROR_CANNOT_OPEN
             << arg
             << DOT
             << endl;
        exit(ERROR_NUMBER_CANNOT_OPEN);
    }
}

// Procesa la opción de línea de comando "-I".
static void opt_implementation(Parameters_Queue_t &args)
{
    // Tomo el único parámetro que
    // necesito ( nombre sel archivo).
    string arg = args.front();

    // Elimino de la cola
    // el parámtro consumido.
    args.pop();

    if (arg == "list")
        implementation = timp_list;
    else if (arg == "tree")
        implementation = timp_tree;
    else if (arg == "hash")
        implementation = timp_hash;
    else
    {
        cerr << ERROR_UNKNOWN_IMPLEMENTATION
             << arg
             << DOT
             << endl;
        exit(ERROR_NUMBER_UNKNOWN_IMPLEMENTATION);
    }
}

// Procesa la opción de línea de comando "-I".
static void opt_type(Parameters_Queue_t &args)
{
    // Tomo el único parámetro que
    // necesito ( nombre sel archivo).
    string arg = args.front();

    // Elimino de la cola
    // el parámtro consumido.
    args.pop();

    if (arg == "int")
        datatype = tdtype_int;
    else if (arg == "float")
        datatype = tdtype_float;
    else if (arg == "string")
        datatype = tdtype_string;
    else
    {
        cerr << ERROR_UNKNOWN_DATA_TYPE
             << arg
             << DOT
             << endl;
        exit(ERROR_NUMBER_UNKNOWN_DATA_TYPE);
    }
}


/*---------------- Funciones de procesado de la línea de comando - End ----------------*/


// Cuerpo del programa.
int main(int argc, char * const argv[])
{
    // Program version.
    ProgramVersion Prog_version = PROGRAM_VERSION;

    // Objeto usado para parsear la línea de comando.
    CmdLine cmdl(PROGRAM_NAME, PROGRAM_DESCRIPTION, Prog_version, NULL);

    ///////////////////////////////////////////////////////////
    // Agrego las opciones usadas por el programa. ////////////
    cmdl.Add_Option(1, "i", "istream", "-",
                    HELP_TEXT_ISTREAM, opt_istream, false);

    cmdl.Add_Option(1, "o", "ostream", "-",
                    HELP_TEXT_OSTREAM, opt_ostream, false);

    cmdl.Add_Option(1, "I", "implementation", "list",
                    HELP_TEXT_IMPLEMENTATION, opt_implementation, false);

    cmdl.Add_Option(1, "t", "type", "int",
                    HELP_TEXT_DATA_TYPE, opt_type, false);
    //////////////////////////////////////////////////////////

    // Parseo la línea de comando.
    cmdl.Parse(argc, argv);	

	// Buffer para lectura línea a línea.
	std::string LineBuffer;

	// Conjunto con el tipo de dato y la estructura de soporte indicada
	// (El wrapper "abstractor" del conjunto en realidad).
	Set_Abstractor Set(implementation , datatype);

	// Parser para los comandos del conjunto.
	Set_Command_Parser Parser;

	// Comando a ejecutar.
	t_commands Command;

	// Parámetro del comando a ejecutar.
	std::string Parameter;

	// Código de error.
	byte Error_Number;

	// Informo la implementación seleccionada.
	//clog << IMPLEMENTING << endl << Set.class_ID() << endl << endl;
	// ELIMINADO PORQUE EN EL ENUNCIADO ESPERA SALIDA VACIA TAMBIEN POR &2.
	
	// Leo línea a línea y proceso los comandos indicados
	// hasta alcanzarse el fin sel stream.
	while(std::getline(*iss, LineBuffer))
	{
		// Parseo línea leída, obteniendo el comando y la representación en 
		// string del parámetro (si existe) y luego lo ejecuto.
		if (((Error_Number = Parser.parse(LineBuffer, Command, Parameter)) != COMMAND_PARSED) ||
			((Error_Number = Set.execute(Command, Parameter, *oss)) != COMMAND_EXECUTED))
		{
			// Se produjo un error, salgo indicándolo.
			cerr << ERROR_EXECUTING_SET_COMMAND << ' ' << get_error_description(Error_Number) << endl;
			exit(ERROR_NUMBER_EXECUTING_SET_COMMAND);
		}
	}

	// Informo que el procesado de la entrada se completó correctamente.
	//clog << endl << COMPLETE << endl << endl;
	// ELIMINADO PORQUE EN EL ENUNCIADO ESPERA SALIDA VACIA TAMBIEN POR &2.

    // El proceso se completó correctamente.
    return ERROR_NUMBER_SUCCESS;   
}
